package com.mimiwang.common.aop;

import com.mimiwang.common.annotation.Limit;
import com.mimiwang.common.constant.HttpStatus;
import com.mimiwang.common.utils.NetworkUtils;
import com.mimiwang.common.utils.ResponseUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;


@Aspect
@Component
@Slf4j
public class LimitAspect {

    @Autowired
    RedisTemplate<String,Object> redisTemplate;

    @Pointcut("@annotation(com.mimiwang.common.annotation.Limit) || @within(com.mimiwang.common.annotation.Limit)")
    void test(){

    }

    @Around("test()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
//        获取方法注解
        Limit annotation = method.getAnnotation(Limit.class);
//        获取注解参数
        String preKey = annotation.key();
        int maxNum = annotation.num();
        int duration = annotation.duration();
        TimeUnit timeUnit = annotation.timeUnit();

//        如果没有设置key，则默认 key=类名:方法名:ip
        if(Strings.isBlank(preKey)){
            preKey=signature.getDeclaringTypeName()+":"+method.getName();
        }
//        获取ServletRequestAttributes
        ServletRequestAttributes requestAttributes =(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        HttpServletResponse response = requestAttributes.getResponse();
//        获取ip
        String ip = NetworkUtils.getIpAddress(request);

        String key= preKey+":"+ip;

//        redis获取
        Object o = redisTemplate.opsForValue().get(key);
//        第一次访问
        if(o == null){
//            设置redis
            redisTemplate.opsForValue().set(key,1,duration,timeUnit);
            log.info("[Limit] [key]:{} |  [Num]:{} |  Status:{} |  Max:{}",key,1,"ok",maxNum);
//            放行
            return joinPoint.proceed(joinPoint.getArgs());
        }else {
            int num=(int)o;
//            达到最大次数，禁止访问
            if( num == maxNum){
                log.error("[Limit] [key]:{} |  [Num]:{} |  Status:{} |  Max:{}",key,num,"Ban",maxNum);
                return this.returnLimit();
            }
//            未达到最大值, 值+1
            Long increment = redisTemplate.opsForValue().increment(key);
            log.info("[Limit] [key]:{} |  [Num]:{} |  Status:{} |  Max:{}",key,increment,"ok",maxNum);
//            放行
            return joinPoint.proceed(joinPoint.getArgs());
        }


    }

//    错误响应
    public String returnLimit() throws IOException {

        HttpStatus MAXLIMIT = HttpStatus.MAXLIMIT;
        return ResponseUtil.errorJSONData(MAXLIMIT.getCode(), MAXLIMIT.getMsg());

    }
}
